package metrics;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DecimalFormat;
import java.util.Collections;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;




public aspect EF_Metrics {


	static private Hashtable <String, Integer> classes = new Hashtable <String, Integer>(); // the hash table
	
	private static int counter = 0;
	private static int totalCount=0;
	private static double percentage=0;
	private static DecimalFormat df = new DecimalFormat("#.#");
	private static final int tabPos = 70;
	private static String Executed_Class;
	static private String appName= "SimpleJavaApp";
	
	pointcut Class_Execution(): execution (* *..* (..)) && !within (DC_Metrics) && !within (EF_Metrics); 
	
	pointcut excluded(): // point cut to exclude unwanted classes
		call(* org.aspectj..*(..)) || call(* java..*(..))  ;
	
	pointcut resutls(): call (* System.exit(..));
	
	
	before  (): Class_Execution() &&  !excluded() {
    
//    caller = thisEnclosingJoinPointStaticPart.getSignature().getDeclaringTypeName(); 
		
		 Executed_Class = thisJoinPoint.getSignature().getDeclaringTypeName();
		//String  Executed_Class = thisJoinPointStaticPart.getSignature().getDeclaringTypeName();

	 //  if(!Executed_Class.contains("$"))
	//	 EF_Metrics e = update(Executed_Class);
	
		System.out.println("Class under test : "+  Executed_Class);
       
		if(!Executed_Class.contains("$")) // to exclude references
		{
			update(Executed_Class);
			}
   
	  
	  }

	
		before  (): resutls(){
			try {
				outputResult(); // execute the output method
				
				// open the output file 
				
				String[] command = { "cmd.exe", "/C", "Start", "D://Experiments/MetricsResults/SimpleJavaApp/"+appName+"_EF_Output.txt" };
					//	Process child = 
				Runtime.getRuntime().exec(command);
				}
				catch (Exception e){
					System.out.println(e);
				}
		//	System.out.println("total number of coupling =" + counter);
			
		}

	

	 public void update (String Executed_Class) // to add the the information to a hashtable
		{
		 	
			int newCounter = 0;
			String clsName = ""; 
			
		 // counter= counter + 1;
			if (!classes.containsKey(Executed_Class)) { // check if the the callee is already exist or not 
				counter +=1;
		       classes.put(Executed_Class, counter);
				System.out.print("\n\n Caller is "+ Executed_Class + " and the counter is "+ counter + " now\n\n" );
				counter=0;
		 		} 
			else { //just add callee to list of callees
				
					newCounter= (int)classes.get(Executed_Class);
					System.out.println("\n\n" +clsName+"\n\n" );

					newCounter +=1;
					classes.put(Executed_Class, newCounter);
			// adds 1 to the counter (whenever there is coupling)
					System.out.println("\n\n Caller is "+ Executed_Class + " and the counter is "+ newCounter + " now\n\n" );

		   }// end of the 2nd if 
			totalCount+=1;
		
		}// end of else 
		
		
		
		
				////////////////////////////////////////////////////
				///          the output display method		    ////
				////////////////////////////////////////////////////
		private void outputResult() {
		
			
		System.out.println("\nResult of EF Metrics");
		System.out.println("Class\t\t\tEF_Metrics\t\t\t%");
		System.out.println("------------------------------------------------------------------");
		
		//sorted output of hashtable
		Vector<String> v = new Vector<String>(classes.keySet());
		Collections.sort(v);

		Iterator<String> it = v.iterator();
		
		////
		//writing results into a file ...
		////
		
		// write into a text file 
		

//		stack = Thread.currentThread ().getStackTrace ();
//	    root = stack[stack.length - 1];
//	    buffer= root.getFileName();
//	    appName = buffer.replaceAll("[.java]", "");
		
		writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_EF_Output.txt", "\t\t\t	"+appName+" Program  \r\n\r\n " , false, true);
		writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_EF_Output.txt", "\t\t\r\r\t     	Results of EF Metrics \r"  , true, true);
		writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_EF_Output.txt", "\n\nClassname  \t\t\t\t\t\t Execution frequency	% \r\n"  , true, true);
		writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_EF_Output.txt", "\t\t\t \r\n" + "===================================================================================== \r\n" , true, true);
		
		while (it.hasNext()) {
		   String clsName = it.next();
		   String printName = pad(clsName, tabPos);
		    percentage = ((double)classes.get(clsName)/totalCount)*100 ;
		    
		    System.out.print(clsName.toString());
		    System.out.print("\t\t" + classes.get(clsName)); 
		    System.out.println("\t\t"+df.format(percentage));
		
		   // use to find the percentage of coupling per each class
		//   percentage = (double)classCouples.get(clsName).size()/(double)counter;
		//   percentage *= 100; 
		  
		//   System.out.println("\t\t the percentage of coupling for this class is " + percentage+"%");
		   
		// System.out.println("\t\t" + classes.get(clsName) + "\n");
		
		 //
		 //writing the output into a text file
		 //
	//	 DecimalFormat df = new DecimalFormat("#.##");
		  
		 
		 writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_EF_Output.txt", "\r\n "+ printName +  classes.get(clsName).toString() +"	"+df.format(percentage) +"%\r\n", true, true);
		
		// writeToFile("D:/Experiments/MetricsResults/Test_Project/Test_EF_Output.txt", "\t\t\t\t " + classes.get(clsName).toString()+ "\t\n", true, true);
		
		// writeToFile("D:/Experiments/MetricsResults/Test_Project/Test_EF_Output", "\r\n\t\t\t\t\t\t\t\t\t " +  df.format(percentage)+ "%\t\n", true, true);
		
		if (it.hasNext())
		{
		 writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_EF_Output.txt", "\r\n--------------------------------------------------------------------------------------", true, true);
		}
		
		
		} // end of the loop 
		 writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_EF_Output.txt", "\t\t\t \r\n\r\n" + "====================================================================================== \r\n" , true, true);

		 writeToFile("D:/Experiments/MetricsResults/SimpleJavaApp/"+appName+"_EF_Output.txt", "\r\r\n\n\n Total Number of execution ="+totalCount, true, true);

		
		}
		
		
		// to retrieve data from the hashtable 
		static String pad(final String str, final int width) 
		{
		StringBuffer buf = new StringBuffer(str);
		int space = width - buf.length();
		while (space-- > 0) {
		   buf.append(' ');
		}
		
		return buf.toString();
		
		}
		
		static DataOutputStream dos;

		// writing the output into a text file
		public boolean writeToFile(String fileName, String dataLine,
			      boolean isAppendMode, boolean isNewLine) {
			    if (isNewLine) {
			      dataLine = "\n" + dataLine;
			    }
		
			    try {
			      File outFile = new File(fileName);
			      if (isAppendMode) {
			        dos = new DataOutputStream(new FileOutputStream(fileName, true));
			      } else {
			        dos = new DataOutputStream(new FileOutputStream(outFile));
			      }
		
			      dos.writeBytes(dataLine);
			      dos.close();
			    } catch (FileNotFoundException ex) {
			      return (false);
			    } catch (IOException ex) {
			      return (false);
			    }
			    return (true);
		
			  }
		
		
		

}

